﻿using Abp.Logging;
using AVIS.OutBound.WizardAgent.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace AVIS.OutBound.WizardAgent
{
    /// <summary>
    /// Wizard缓存帮助类
    /// </summary>
    public class WizardCacheHelper
    {
        /// <summary>
        /// 获取Wizard接口的
        /// </summary>
        /// <param name="requestXml"></param>
        /// <param name="request"></param>
        /// <param name="funcLogic"></param>
        /// <returns></returns>
        public static TResponse GetResponseXml<TResponse>(IRequest request, Func<string, string> funcLogic = null)
            where TResponse : IResponse, new()
        {
            TResponse response = new TResponse();
            try
            {
                // 记录请求日志
                AddRequestXmlLog(request.ToRequestXml());

                // 精确计算执行时间
                Stopwatch sw = new Stopwatch();
                sw.Start();

                // 判断是否需要缓存，如果缓存Key不为空，则需要缓存
                var cacheRequest = request as ICacheRequest;
                string cacheKey = cacheRequest != null ? cacheRequest.GetCacheKey() : "";
                if (!string.IsNullOrWhiteSpace(cacheKey))
                {
                    // 如果缓存Key存在且不过期，则直接读取缓存XML
                    string xmlDoc = RedisHelper.StringGet(cacheKey);
                    if (!string.IsNullOrWhiteSpace(xmlDoc))
                    {
                        response.ResponseString = xmlDoc;
                        sw.Stop();
                        HandleResponseXml(response);
                        AddResponseXmlLog(response);
                        WizardLogHelper.AddWizardMonitorLog(true, sw.Elapsed.TotalSeconds, cacheKey);
                    }
                }

                // 如果缓存没有，则直接请求Wizard接口
                if ((string.IsNullOrEmpty(response.ResponseString) || response.HasError) && funcLogic != null)
                {
                    try
                    {
                        response = new TResponse();
                        sw.Reset();
                        sw.Start();
                        response.ResponseString = funcLogic(request.ToRequestXml());
                        sw.Stop();

                        // 处理返回结果
                        HandleResponseXml(response);

                        // 将返回的xml信息存放到Redis中
                        if (!response.HasError && !string.IsNullOrWhiteSpace(cacheKey))
                        {
                            RedisHelper.StringSet(cacheKey, response.ResponseString, cacheRequest.PickupDateTime - Abp.Timing.Clock.Now);
                            //LogHelper.Logger.InfoFormat($"更新Redis：{response.ResponseString}");
                        }

                        AddResponseXmlLog(response);
                        WizardLogHelper.AddWizardMonitorLog(false, sw.Elapsed.TotalSeconds, cacheKey);
                    }
                    catch (Exception ex)
                    {
                        throw new WizardException("请求Wizard接口失败。", ex);
                    }
                }

                return response;
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("请求对象为：" + Newtonsoft.Json.JsonConvert.SerializeObject(request), ex);

                response.ErrorMessage = ex.Message;
                return response;
            }
        }

        private static void HandleResponseXml(IResponse response)
        {
            if (string.IsNullOrWhiteSpace(response.ResponseString))
            {
                response.ErrorMessage = "请求超时，请刷新重试！";
                return;
            }

            var responseXDoc = XDocument.Parse(response.ResponseString);
            var XServerError = XmlHelpler.FindXElement(responseXDoc.Root, "faultstring", "");
            if (XServerError != null)
            {
                /*
                 <env:Fault>
                   <faultcode>env:Server</faultcode>
                   <faultstring>Internal Error (from server)</faultstring>
                 </env:Fault>
                 */
                response.ErrorMessage = XmlHelpler.ParseValue(XServerError) + "。请求Wizard接口失败。";
                return;
            }

            var XStandardError = XmlHelpler.FindXElement(responseXDoc.Root, "StandardError", "");
            var XError = XmlHelpler.FindXElement(responseXDoc.Root, "Error", WizardConsts.XNamespace);

            // 错误信息
            if (XError != null)
            {
                response.ErrorCode = int.Parse(XmlHelpler.ParseValue(XError, "ShortText").Substring(0, 5));
                response.ErrorMessage = XmlHelpler.ParseValue(XError);
                return;
            }
            else if (XStandardError != null)
            {
                response.ErrorMessage = XmlHelpler.ParseValue(XStandardError);
                return;
            }

            // 提醒信息
            var XWarning = XmlHelpler.FindXElement(responseXDoc.Root, "Warning", WizardConsts.XNamespace);
            if (XWarning != null)
            {
                response.WarningCode = int.Parse(XmlHelpler.ParseValue(XWarning, "RecordID").Substring(0, 5));
                response.WarningMessage = XmlHelpler.ParseValue(XWarning);
                return;
            }
        }

        /// <summary>
        /// 移除缓存的Xml文件
        /// </summary>
        /// <remarks>如果选择车组时保存的价格跟获取增值服务返回的价格不一致，则移除该xml缓存文件</remarks>
        /// <param name="request"></param>
        public static void RemoveCacheXml(ICacheRequest request)
        {
            var cacheKey = request.GetCacheKey();
            if (RedisHelper.KeyExists(cacheKey))
            {
                RedisHelper.KeyDelete(cacheKey);
            }
            else
            {
                // 可能是默认套餐，RateCode为空
                request.RateCode = "";
                cacheKey = request.GetCacheKey();
                RedisHelper.KeyDelete(cacheKey);
            }
        }

        #region 私有方法和属性
        protected static readonly log4net.ILog _LogWizard = log4net.LogManager.GetLogger("WizardLog");

        /// <summary>
        /// 插入Wizard接口的响应XML日志
        /// </summary>
        /// <param name="responseXml"></param>
        /// <param name="filePath"></param>
        private static void AddResponseXmlLog(IResponse response)
        {
            // 保存到通用日志文件
            _LogWizard.InfoFormat("Wizard接口（{1}）返回的Xml：<br />{0}", FormatLogXml(response.ResponseString), response.ResponseName);
        }

        /// <summary>
        /// 插入Wizard接口的请求XML日志
        /// </summary>
        /// <param name="requestXml"></param>
        private static void AddRequestXmlLog(string requestXml)
        {
            _LogWizard.InfoFormat("请求Wizard接口的Xml：<br />{0}", FormatLogXml(requestXml));
        }

        /// <summary>
        /// 格式化xml
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        private static string FormatLogXml(string xml)
        {
            if (string.IsNullOrWhiteSpace(xml))
                return string.Empty;

            var logMessage = xml.Replace("<", "&lt;").Replace(">", "&gt;").Replace("\"", "&quot;");

            // 高亮显示关键字
            logMessage = logMessage.Replace("OTA_VehAvailRateRQ ", "<strong style=\"color:red\">OTA_VehAvailRateRQ </strong>");
            logMessage = logMessage.Replace("OTA_VehAvailRateRS ", "<strong style=\"color:red\">OTA_VehAvailRateRS </strong>");
            logMessage = logMessage.Replace("OTA_VehRateRuleRQ ", "<strong style=\"color:red\">OTA_VehRateRuleRQ </strong>");
            logMessage = logMessage.Replace("OTA_VehRateRuleRS ", "<strong style=\"color:red\">OTA_VehRateRuleRS </strong>");
            logMessage = logMessage.Replace("OTA_VehResRQ ", "<strong style=\"color:red\">OTA_VehResRQ </strong>");
            logMessage = logMessage.Replace("OTA_VehResRS ", "<strong style=\"color:red\">OTA_VehResRS </strong>");

            return string.Format("<pre>{0}</pre>", logMessage);
        }
        #endregion
    }
}